โ– humdrum codex / sportsball
license AGPL-3.0
3.5 KB raw
id
TASK-003
title
Favorite teams
status
๐Ÿ Done
assignee
@humdrum-tiv
created_date
2026-06-16 18:03
updated_date
2026-06-18 01:17
labels
feature
dependencies
priority
high
ordinal
3000

Description

User marks favorite teams. Favorites float to the top of the main dashboard view. In a league view, always show each favorite's last result and next fixture, even outside the current window.

Acceptance Criteria

Implementation Plan

Depends on TASK-004 config layer + team identity.

  1. model.Team gains ID string (ESPN team id); espn teamJSON.ID + mapTeam set it.
  2. App: cfg config.Config + favs map[string]bool (key=league+':'+teamID, fallback abbr). New() loads via config.Load(). Helpers favKey(league,team)/isFav(league,team)/toggleFav(league,team){mutate favs+cfg, config.Save best-effort}.
  3. Marking [detail view]: keys Fav(f)=toggle away, FavHome(F)=toggle home. Footer hint. Persist on toggle [AC#3].
  4. Cards: renderCard/teamRow take fav bool -> show โ˜… before favorite team abbr (components.go). bodyLines passes a.isFav per side.
  5. Ordering (sections.go) [AC#1/#2]:
    • all-leagues: prepend 'โ˜… Favorites' section = today's games across leagues involving a favorite; remove those from their per-league section (no dup).
    • single-league: prepend 'โ˜… Favorites' bucket before Today/Upcoming/Past = for each favorite team in league, its live game + most-recent final (last) + next scheduled (next) found within fetched window; dedup.
    • sections()/bodyLines() invariant preserved (bodyLines re-walks sections()).
  6. Out-of-window last/next (favorite's next match >7d away or last >2d ago) deferred to follow-up using ESPN team schedule endpoint -> new task.
  7. Tests: favKey stability; favorites-section selection from a fixture set.

Final Summary

Favorite teams: mark, persist, pin to top.

What changed:

Scope note: last/next resolve within the per-league fetch window; matches further out are deferred to TASK-009 (per-team schedule endpoint).

Also in this branch (window infra): per-league fetch windows on model.League (MLB 3/5d, NFL 10/10d, soccer/default 5/10d) and a required &limit=300 on ScoreboardRange โ€” the latter also fixes a latent bug where dense slates were silently truncated to ESPN's default cap.

Tests: ui/favorites_test.go (favKey identity, hasFav, favoriteHighlights live/next/last selection + dedup + non-fav exclusion). All packages build/vet/test clean.